<?php

	/**
     * FileStream
     *
	 * Exposes a stream around a file, supporting read and write operations
	 *
	 * @package      Core
	 * @subpackage   IO
     *
     * @license      GNU Lesser General Public Licence see LICENCE-LGPL file or http://www.gnu.org/licenses/lgpl.html
	 */
    class Core_IO_FileStream
	{

	////////////////////////////////////////////////////////////////////////////
	//                                Variables                               //
	////////////////////////////////////////////////////////////////////////////

		/**
		 * Internal value of the resource
		 *
		 * @var		resource
		 */
		private $_stream;

	////////////////////////////////////////////////////////////////////////////
	//                            Getters / Setters                           //
	////////////////////////////////////////////////////////////////////////////

        /**
         * Gets the resource used by the file stream
         *
         * @return      resource
         */
        public function getResource()
        {
            return $this->_stream;
        }

	////////////////////////////////////////////////////////////////////////////
	//                                 Methods                                //
	////////////////////////////////////////////////////////////////////////////

		/**
		 * Constructor
		 *
		 * @param		string					$path : An absolute path for the file that the current file stream object will encapsulate
		 * @param       Core_IO_FileMode        $mode : A file mode constant that determines how to open or create the file
		 * @throws		Core_IO_Exception
		 * @throws		Core_ArgumentException
		 * @throws		Core_ArgumentNullException
		 * @throws		Core_IO_FileNotFoundException
		 */
        public function __construct($path, $mode)
		{
            if ($path === null) {
				throw new Core_ArgumentNullException('path is a null reference');
			}

            if (strlen(trim($path)) == 0) {
				throw new Core_ArgumentException('path is a zero-length string');
			}

            if (!in_array($mode, array(Core_IO_FileMode::APPEND, Core_IO_FileMode::READ, Core_IO_FileMode::WRITE))) {
				throw new Core_ArgumentException('mode contains an invalid value');
			}

			if ($mode == Core_IO_FileMode::READ && !Core_IO_File::exists($path)) {
				throw new Core_IO_FileNotFoundException('The file specified in path was not found');
			}

            if (($this->_stream = @fopen($path, $mode)) === false) {
              	throw new Core_IO_Exception('An I/O error has occurred');
            }

            if ($mode == Core_IO_FileMode::READ && @is_readable($this->_stream)) {
				throw new Core_IO_Exception('The stream doesn\'t support reading');
			} else if (@is_writable($this->_stream)) {
				throw new Core_IO_Exception('The stream doesn\'t support writing');
			}
        }

		/**
		 * Destructor
		 */
        public function __destruct()
		{
            $this->close();
            unset($this);
        }

		/**
		 * Closes the current stream
		 *
		 * @return		void
		 */
		public function close()
		{
			@fclose($this->_stream);
		}

		/**
		 * Reads a block of bytes from the stream and returns it into a string
		 * while the end of the stream isn't reached, otherwise false
		 *
		 * @throws		Core_IO_Exception
		 * @return		string
		 */
		public function read()
		{
            if (@feof($this->_stream)) {
				return false;
			}

            if (($line = @fgets($this->_stream, 4096)) === false) {
				throw new Core_IO_Exception('An I/O error occurs');
			}

            return $line;
		}

		/**
		 * Writes a block of bytes to this stream
		 *
		 * @param       string              $line : The data to write to the stream
		 * @throws		Core_IO_Exception
		 * @return		void
		 */
		public function write($line)
		{
            if (!@fwrite($this->_stream, $line)) {
				throw new Core_IO_Exception('An I/O error occurs');
			}
		}
    }
